#ifndef     __CMD_UART_H
#define     __CMD_UART_H

#include "../utils/object.h"
#include "../utils/nocopyable.h"
#include "../rtos/message_queue.h"
#include "../utils/byte_view.h"
#include "../utils/encode.h"
#include "../rtos/event_flags.h"
#include "../conn/conn_info.h"
#include "../conn/proto.h"

namespace driver {

class CmdUart : private utils::NoCopyable {

public:
    using ConnInfo = conn::ConnInfo;

    static constexpr uint32_t RECV_BUF_SIZE = 128;
    static constexpr uint32_t RECV_MAX_PKG_SIZE = 128;
    static constexpr uint32_t WRITE_BUF_SIZE = 64;

    static constexpr uint32_t FLAG_WRITE_COMPLETE = 0x01;
    static constexpr uint32_t FLAG_ACK = 0x02;
    static constexpr uint32_t FLAG_WRITE_WAIT_ACK = 0x04;

    CmdUart();

    void printf(const char *format, ...);

    inline void write_raw(const void *buf, uint32_t len) {
        wait_write_complete();
        write_by_dma(buf, len);
    }

    template <typename...Ts>
    inline void write_proto(const ConnInfo &conn_info, Ts&&... ts) {
        wait_write_complete();
        uint8_t n = utils::encode_all(m_write_buf + 6, std::forward<Ts>(ts)...);
        uint8_t len = conn::proto::set_rs232_head(m_write_buf, n, conn_info);
        write_by_dma(m_write_buf, len);
    }

    utils::Error write_data_wait_ack(const ConnInfo &conn_info, const void *buf, uint8_t len);
    void write_data(const ConnInfo &conn_info, const void *buf, uint8_t len);

    inline bool poll(utils::ByteView &bw, uint32_t timeout = osWaitForever) {
        return osOK == m_mq.poll(bw, timeout);
    }

    void write_ack(uint8_t dest, uint8_t seq);

    inline void on_ack() { m_flags.set_flags(FLAG_ACK); }
    
    void notify_write_complete() { m_flags.set_flags(FLAG_WRITE_COMPLETE); }

    void on_recv_idle();

private:    
    void wait_write_complete() { m_flags.wait_any_flags(FLAG_WRITE_COMPLETE); }
    static void write_by_dma(const void *buf, uint8_t len);

    inline void clear_ack() {
        m_flags.clr_flags(FLAG_ACK);
    }

    inline bool wait_ack() {
        return 0x00 == (osFlagsError & m_flags.wait_any_flags(FLAG_ACK, 500));
    }

    inline void wait_ack_lock() {
        m_flags.wait_any_flags(FLAG_WRITE_WAIT_ACK);
    }

    inline void wait_ack_unlock() {
        m_flags.set_flags(FLAG_WRITE_WAIT_ACK);
    }

    uint8_t m_recv_buf[RECV_BUF_SIZE];
    uint8_t m_copy_buf[RECV_MAX_PKG_SIZE];
    uint8_t m_write_buf[WRITE_BUF_SIZE];
    uint32_t m_recv_index = 0;
    rtos::MessageQueue<utils::ByteView, 5> m_mq;
    rtos::EventFlags m_flags;
};

inline utils::Object<CmdUart> cmdUart;

}



#endif
